package com.sphsine.bip;

import com.javaweb.common.config.CommonConfig;
import com.javaweb.common.utils.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

public class SHYPRequest extends BaseRequest{
    private DDKJConfig config;
    public SHYPRequest(DDKJConfig config) throws Exception{

        this.config = config;
    }

    /**
     * 请求，只请求一次，不做重试
     * @param domain
     * @param urlSuffix
     * @param uuid
     * @param data
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @param useCert 是否使用证书，针对退款、撤销等操作
     * @return
     * @throws Exception
     */
    private String requestOnce(final String domain, String urlSuffix, String uuid, Map<String, String> param, String postData, int connectTimeoutMs, int readTimeoutMs, boolean useCert, SHYPConstants.RequestMethod method) throws Exception {
        //
        BasicHttpClientConnectionManager connManager = super.getHttpClientConnectionManager(useCert);
        if(param!=null) {
            Set<String> keySet = param.keySet();
            String[] keyArray = keySet.toArray(new String[keySet.size()]);
            //Arrays.sort(keyArray);
            StringBuilder sb = new StringBuilder();
            for (String k : keyArray) {
                sb.append(k).append("=").append(param.get(k).trim()).append("&");
            }
            sb.deleteCharAt(sb.length() - 1);
        }

        HttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(connManager)
                .build();

        String url = "https://" + domain + urlSuffix;
        HttpRequestBase httpPost;
        if(method.equals(SHYPConstants.RequestMethod.GET)) {
            httpPost = new HttpGet(url);
        }
        else
        {
            httpPost = new HttpPost(url);
        }
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
        httpPost.setConfig(requestConfig);

        if(!StringUtils.isEmpty(postData)) {
            StringEntity postEntity = new StringEntity(postData, "UTF-8");
            httpPost.addHeader("Content-Type", "application/json");
            //httpPost.addHeader("User-Agent", USER_AGENT + " " + config.getMchID());
            ((HttpPost) httpPost).setEntity(postEntity);
        }

        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        return EntityUtils.toString(httpEntity, "UTF-8");

    }


    public String request(String urlSuffix, String uuid, Map<String, String> param, String postData,int connectTimeoutMs, int readTimeoutMs, boolean sandBox, SHYPConstants.RequestMethod method) throws Exception {
        Exception exception = null;
        long elapsedTimeMillis = 0;
        long startTimestampMs = SHYPUtil.getCurrentTimestampMs();
        boolean firstHasDnsErr = false;
        boolean firstHasConnectTimeout = false;
        boolean firstHasReadTimeout = false;
        ISHYPDomain.DomainInfo domainInfo = config.getSHYPDomain().getDomain(config,sandBox);
        if(domainInfo == null){
            throw new Exception("SHYPConfig.getSHYPDomain().getDomain() is empty or null");
        }
        try {
            String result = requestOnce(domainInfo.domain, urlSuffix, uuid, param,postData, connectTimeoutMs, readTimeoutMs, false,method);
            return result;
        }
        catch (UnknownHostException ex) {  // dns 解析错误，或域名不存在
            exception = ex;
            firstHasDnsErr = true;
            elapsedTimeMillis = SHYPUtil.getCurrentTimestampMs()-startTimestampMs;
            SHYPUtil.getLogger().warn("UnknownHostException for domainInfo {}", domainInfo);
        }
        catch (ConnectTimeoutException ex) {
            exception = ex;
            firstHasConnectTimeout = true;
            elapsedTimeMillis = SHYPUtil.getCurrentTimestampMs()-startTimestampMs;
            SHYPUtil.getLogger().warn("connect timeout happened for domainInfo {}", domainInfo);
        }
        catch (SocketTimeoutException ex) {
            exception = ex;
            firstHasReadTimeout = true;
            elapsedTimeMillis = SHYPUtil.getCurrentTimestampMs()-startTimestampMs;
            SHYPUtil.getLogger().warn("timeout happened for domainInfo {}", domainInfo);
        }
        catch (Exception ex) {
            exception = ex;
            elapsedTimeMillis = SHYPUtil.getCurrentTimestampMs()-startTimestampMs;
        }
        //config.getSHYPDomain().report(domainInfo.domain, elapsedTimeMillis, exception);
        throw exception;
    }


    /**
     * 可重试的，非双向认证的请求
     * @param urlSuffix
     * @param uuid
     * @param data
     * @return
     */
    public String requestWithoutCert(String urlSuffix, String uuid, Map<String, String>  data, boolean autoReport, SHYPConstants.RequestMethod method) throws Exception {
        return this.request(urlSuffix, uuid, data,"", config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs(), false, method);
    }

    /**
     * 可重试的，非双向认证的请求
     * @param urlSuffix
     * @param uuid
     * @param data
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @return
     */
    public String requestWithoutCert(String urlSuffix, String uuid, Map<String, String> param,int connectTimeoutMs, int readTimeoutMs,  boolean autoReport) throws Exception {
        return this.request(urlSuffix, uuid, param,"",connectTimeoutMs, readTimeoutMs, false,SHYPConstants.RequestMethod.POST);
    }

    public String postData(String urlSuffix, String uuid, Map<String, String> param,String postData, boolean sandBox) throws Exception {
        return this.request(urlSuffix, uuid, param, postData,config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs(), sandBox,SHYPConstants.RequestMethod.POST);
    }
    /**
     * 可重试的，双向认证的请求
     * @param urlSuffix
     * @param uuid
     * @param data
     * @return
     */
    public String requestWithCert(String urlSuffix, String uuid, Map<String, String> data, boolean autoReport) throws Exception {
        return this.request(urlSuffix, uuid, data, "",config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs(), true,SHYPConstants.RequestMethod.POST);
    }

    /**
     * 可重试的，双向认证的请求
     * @param urlSuffix
     * @param uuid
     * @param data
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @return
     */
    public String requestWithCert(String urlSuffix, String uuid, Map<String, String> data, int connectTimeoutMs, int readTimeoutMs, boolean autoReport) throws Exception {
        return this.request(urlSuffix, uuid, data, "",connectTimeoutMs, readTimeoutMs, true,SHYPConstants.RequestMethod.POST);
    }
}

